package shoppingcart

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"mall/service/forum/api/internal/svc"

	"github.com/sirupsen/logrus"
)

type (
	// ShoppingCart is an interface to be customized, add more methods here,
	// and implement the added methods in customShoppingCart.
	ShoppingCart interface {
		Add(userId string, objectId string, number int64) (int64, error)         // 加入
		Delete(userId string, objectId string) (int64, error)                    // 删除
		BatchDelete(userId string, objectIds []string) (int64, error)            // 批量删除
		Clear(userId string) (int64, error)                                      // 清空
		List(userId string) ([]*ShoppingCartItem, error)                         // 对性能有影响，生产环境慎用
		Quantity(userId string, objectId string) (int64, error)                  // 单商品数量
		Count(userId string) (int64, error)                                      // 总数
		InitNumber(userId string, objectId string, number int64) (bool, error)   // 初始化单商品数量
		UpdateNumber(userId string, objectId string, number int64) (bool, error) // 从数据库同步
		InitCount(userId string, count int64) (bool, error)                      // 初始化总数
		UpdateCount(userId string, count int64) (bool, error)                    // 从数据库同步
	}

	customShoppingCart struct {
		ctx      context.Context
		svcCtx   *svc.ServiceContext
		redisKey string
	}
)

func NewShoppingCart(ctx context.Context, svcCtx *svc.ServiceContext, redisKey string) ShoppingCart {
	return &customShoppingCart{
		ctx:      ctx,
		svcCtx:   svcCtx,
		redisKey: redisKey,
	}
}

func (l *customShoppingCart) getItemKey(userId string, objectId string) string {
	return fmt.Sprintf("{%s:%s}:%s", l.redisKey, userId, objectId)
}

func (l *customShoppingCart) getItemKeyPrefix(userId string) string {
	return fmt.Sprintf("{%s:%s}:", l.redisKey, userId)
}

func (l *customShoppingCart) getSetKey(userId string) string {
	return fmt.Sprintf("{%s:%s}:*", l.redisKey, userId)
}

func (l *customShoppingCart) getCountKey(userId string) string {
	return fmt.Sprintf("{%s:%s}", l.redisKey, userId)
}

func (l *customShoppingCart) Add(userId string, objectId string, number int64) (int64, error) { // #24.07.29#
	res, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaAddScript, []string{
		l.getItemKey(userId, objectId),
		l.getCountKey(userId),
	}, strconv.FormatInt(number, 10))
	if err != nil {
		logrus.Info(fmt.Sprintf("ShoppingCart Add err : %+v ", err))
		return -1, err
	}

	// logrus.Info(fmt.Sprintf("ShoppingCart Add val : %+v ", res))

	if val, ok := res.(int64); ok {
		return val, nil
	}

	return -1, nil
}

func (l *customShoppingCart) Delete(userId string, objectId string) (int64, error) {
	val, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaDeleteScript, []string{
		l.getItemKey(userId, objectId),
		l.getCountKey(userId),
	})
	if err != nil {
		logrus.Info(fmt.Sprintf("ShoppingCart Delete err : %+v ", err))
		return -1, err
	}

	logrus.Info(fmt.Sprintf("ShoppingCart Delete val : %+v ", val))

	if s, ok := val.(string); ok {
		// return strconv.ParseInt(s, 10, 64)

		if len(s) == 0 {
			return 0, nil
		}

		if code, err := strconv.ParseInt(s, 10, 64); err == nil {
			return code, nil
		}

		return 0, nil
	}

	return -1, nil
}

func (l *customShoppingCart) BatchDelete(userId string, objectIds []string) (int64, error) {
	var keys []any
	for _, objectId := range objectIds {
		keys = append(keys, l.getItemKey(userId, objectId))
	}

	val, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaBatchDeleteScript, []string{
		l.getCountKey(userId),
	}, keys...)
	if err != nil {
		logrus.Info(fmt.Sprintf("ShoppingCart BatchDelete err : %+v ", err))
		return -1, err
	}

	logrus.Info(fmt.Sprintf("ShoppingCart BatchDelete val : %+v ", val))

	if s, ok := val.(string); ok {
		// return strconv.ParseInt(s, 10, 64)

		if len(s) == 0 {
			return 0, nil
		}

		if code, err := strconv.ParseInt(s, 10, 64); err == nil {
			return code, nil
		}

		return 0, nil
	}

	return -1, nil
}

func (l *customShoppingCart) Clear(userId string) (int64, error) {
	res, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaClearScript, []string{
		l.getSetKey(userId),
		l.getCountKey(userId),
	})
	if err != nil {
		logrus.Info(fmt.Sprintf("ShoppingCart Clear err : %+v ", err))
		return -1, err
	}

	logrus.Info(fmt.Sprintf("ShoppingCart Clear val : %+v ", res))

	if val, ok := res.(int64); ok {
		return val, nil
	}

	return -1, nil
}

func (l *customShoppingCart) List(userId string) ([]*ShoppingCartItem, error) {
	res, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaListScript, []string{
		l.getSetKey(userId),
	})
	if err != nil {
		logrus.Info(fmt.Sprintf("ShoppingCart List err : %+v ", err))
		return nil, err
	}

	var items []*ShoppingCartItem
	if vals, ok := res.([]any); ok {
		var prefix string = l.getItemKeyPrefix(userId)
		for idx, val := range vals {
			if idx%2 == 1 {
				var id int64
				var qty int32
				if s, ok := vals[idx-1].(string); ok {
					var key string = strings.Replace(s, prefix, "", 1)
					if i, err := strconv.ParseInt(key, 10, 64); err == nil && i > 0 {
						id = i
					}
				}
				if id == 0 {
					continue
				}

				if s, ok := val.(string); ok {
					if i, err := strconv.ParseInt(s, 10, 64); err == nil && i > 0 {
						qty = int32(i)
					}
				}
				items = append(items, &ShoppingCartItem{
					Id:     id,
					Number: qty,
				})
			}
		}
	}

	return items, nil
}

func (l *customShoppingCart) Quantity(userId string, objectId string) (int64, error) {
	val, err := l.svcCtx.BizRedis.GetCtx(l.ctx, l.getItemKey(userId, objectId))
	if err != nil {
		return -1, err
	}

	if len(val) == 0 {
		return 0, nil
	}

	if qty, err := strconv.ParseInt(val, 10, 64); err == nil && qty > 0 {
		return qty, nil
	}

	return 0, nil
}

func (l *customShoppingCart) Count(userId string) (int64, error) { // #24.07.30#
	val, err := l.svcCtx.BizRedis.GetCtx(l.ctx, l.getCountKey(userId))
	if err != nil {
		logrus.Info(fmt.Sprintf("ShoppingCart Count err : %+v ", err))
		return 0, err
	}

	if len(val) == 0 {
		return 0, nil
	}

	if cnt, err := strconv.ParseInt(val, 10, 64); err == nil && cnt > 0 {
		return cnt, nil
	}

	return 0, nil
}

func (l *customShoppingCart) InitNumber(userId string, objectId string, number int64) (bool, error) {
	res, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaInitScript, []string{
		l.getItemKey(userId, objectId),
	}, strconv.FormatInt(number, 10))
	if err != nil {
		logrus.Info(fmt.Sprintf("ShoppingCart InitNumber err : %+v ", err))
		return false, err
	}

	if val, ok := res.(int64); ok && val > 0 {
		return true, nil
	}

	return false, nil
}

func (l *customShoppingCart) UpdateNumber(userId string, objectId string, number int64) (bool, error) {
	res, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaUpdateScript, []string{
		l.getItemKey(userId, objectId),
	}, strconv.FormatInt(number, 10))
	if err != nil {
		logrus.Info(fmt.Sprintf("ShoppingCart UpdateNumber err : %+v ", err))
		return false, err
	}

	if val, ok := res.(int64); ok && val > 0 {
		return true, nil
	}

	return false, nil
}

func (l *customShoppingCart) InitCount(userId string, count int64) (bool, error) {
	res, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaInitScript, []string{
		l.getCountKey(userId),
	}, strconv.FormatInt(count, 10))
	if err != nil {
		logrus.Info(fmt.Sprintf("ShoppingCart InitCount err : %+v ", err))
		return false, err
	}

	if val, ok := res.(int64); ok && val > 0 {
		return true, nil
	}

	return false, nil
}

func (l *customShoppingCart) UpdateCount(userId string, count int64) (bool, error) {
	res, err := l.svcCtx.BizRedis.EvalCtx(l.ctx, luaUpdateScript, []string{
		l.getCountKey(userId),
	}, strconv.FormatInt(count, 10))
	if err != nil {
		logrus.Info(fmt.Sprintf("ShoppingCart UpdateCount err : %+v ", err))
		return false, err
	}

	if val, ok := res.(int64); ok && val > 0 {
		return true, nil
	}

	return false, nil
}
